
package com.hlkj.pay.app.merchant.ext.impl.fuyou;
/*
 * Hlpay-Plus aggregate payment system.
 * Copyright (c) 2024-2025 Hlpay Team Copyright has the right of final interpretation.
 */

import cn.hutool.core.bean.BeanUtil;
import com.hlkj.framework.common.pojo.CommonResult;
import com.hlkj.framework.common.util.json.JsonUtils;
import com.hlkj.framework.extension.annotation.Extension;
import com.hlkj.framework.extension.executor.ExtensionExecutor;
import com.hlkj.framework.extension.scenario.BizScenario;
import com.hlkj.pay.app.merchant.ext.ChannelMerchantOrderExtPt;
import com.hlkj.pay.app.merchant.ext.MerchantExtConstant;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelPayOrderContext;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelPayOrderQueryContext;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelRefundPayOrderContext;
import com.hlkj.pay.app.merchant.ext.impl.fuyou.dto.FuYouConfig;
import com.hlkj.pay.app.merchant.ext.impl.fuyou.dto.FuYouPayOrderResultDto;
import com.hlkj.pay.app.merchant.ext.impl.fuyou.utils.SignUtils;
import com.hlkj.pay.common.CommonResultCode;
import com.hlkj.pay.common.MerchantResultCode;
import com.hlkj.pay.dto.order.PayOrderChannelResultDto;
import com.hlkj.pay.dto.order.notify.CsOrderNotifyDto;
import com.hlkj.pay.dto.order.notify.RefundOrderNotifyDto;
import com.hlkj.pay.enums.PayEnums;
import com.hlkj.pay.enums.PayOrderEnums;
import com.hlkj.pay.infrastructure.model.order.PayOrderDO;
import com.hlkj.pay.infrastructure.model.pay.PayWayDO;
import com.hlkj.pay.infrastructure.model.pay.ProviderChannelConfigDO;
import com.hlkj.pay.util.DateUtils;
import com.hlkj.pay.util.MerchantUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Map;

/**
 * @author HlpayTeam
 * @date 2024/10/15 18:16
 */
@Slf4j
@Repository
@RequiredArgsConstructor
@Extension(bizId = MerchantExtConstant.CHANNEL_FU_YOU)
public class FuYouMerchantOrderExtPt implements ChannelMerchantOrderExtPt {

    private final ExtensionExecutor extensionExecutor;

    @Value("${hlkj.pay.openapi.checkSign:true}")
    private boolean checkSign;

    @Override
    public CommonResult<String> channelOrderCode(ChannelPayOrderContext channelPayOrderContext) {
        PayWayDO payWayDO = channelPayOrderContext.getPayWayDO();
        String fouYouPay = fouYouPay(payWayDO);
        if (!StringUtils.hasText(fouYouPay)) {
            channelPayOrderContext.setRemark("当前支付方式不支持");
            channelPayOrderContext.setProcessStatus(Boolean.FALSE);
            return null;
        }
        return extensionExecutor.execute(FuYouScenePayOrderExtPt.class, BizScenario.valueOf(fouYouPay), ex -> ex.channelOrderCode(channelPayOrderContext));
    }

    @Override
    public void submitChannelPayOrder(ChannelPayOrderContext channelPayOrderContext) {
        PayWayDO payWayDO = channelPayOrderContext.getPayWayDO();
        String fouYouPay = fouYouPay(payWayDO);
        if (!StringUtils.hasText(fouYouPay)) {
            channelPayOrderContext.setRemark("当前支付方式不支持");
            channelPayOrderContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(FuYouScenePayOrderExtPt.class, BizScenario.valueOf(fouYouPay), ex -> ex.submitChannelPayOrder(channelPayOrderContext));

    }

    @Override
    public void queryChannelPayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        PayWayDO payWayDO = channelPayOrderQueryContext.getPayWayDO();
        String huiFuPay = fouYouPay(payWayDO);
        if (!StringUtils.hasText(huiFuPay)) {
            channelPayOrderQueryContext.setRemark("当前支付方式不支持");
            channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(FuYouScenePayOrderExtPt.class, BizScenario.valueOf(huiFuPay), ex -> ex.queryChannelPayOrder(channelPayOrderQueryContext));

    }

    @Override
    public void closeChannelPayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        PayWayDO payWayDO = channelPayOrderQueryContext.getPayWayDO();
        String huiFuPay = fouYouPay(payWayDO);
        if (!StringUtils.hasText(huiFuPay)) {
            channelPayOrderQueryContext.setRemark("当前支付方式不支持");
            channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(FuYouScenePayOrderExtPt.class, BizScenario.valueOf(huiFuPay), ex -> ex.closeChannelPayOrder(channelPayOrderQueryContext));

    }

    @Override
    public void queryChannelClosePayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        PayWayDO payWayDO = channelPayOrderQueryContext.getPayWayDO();
        String huiFuPay = fouYouPay(payWayDO);
        if (!StringUtils.hasText(huiFuPay)) {
            channelPayOrderQueryContext.setRemark("当前支付方式不支持");
            channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(FuYouScenePayOrderExtPt.class, BizScenario.valueOf(huiFuPay), ex -> ex.queryChannelClosePayOrder(channelPayOrderQueryContext));

    }

    @Override
    public boolean refundApply(ChannelRefundPayOrderContext channelRefundPayOrderContext) {
        PayWayDO payWayDO = channelRefundPayOrderContext.getPayWayDO();
        String fouYouPay = fouYouPay(payWayDO);
        if (!StringUtils.hasText(fouYouPay)) {
            channelRefundPayOrderContext.setRemark("当前支付方式不支持");
            channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
            return false;
        }
        return extensionExecutor.execute(FuYouScenePayOrderExtPt.class, BizScenario.valueOf(fouYouPay), ex -> ex.refundApply(channelRefundPayOrderContext));
    }

    @Override
    public void refundChannelPayOrder(ChannelRefundPayOrderContext channelRefundPayOrderContext) {
        PayWayDO payWayDO = channelRefundPayOrderContext.getPayWayDO();
        String huiFuPay = fouYouPay(payWayDO);
        if (!StringUtils.hasText(huiFuPay)) {
            channelRefundPayOrderContext.setRemark("当前支付方式不支持");
            channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(FuYouScenePayOrderExtPt.class, BizScenario.valueOf(huiFuPay), ex -> ex.refundChannelPayOrder(channelRefundPayOrderContext));

    }

    @Override
    public void queryRefundChannelPayOrder(ChannelRefundPayOrderContext channelRefundPayOrderContext) {
        PayWayDO payWayDO = channelRefundPayOrderContext.getPayWayDO();
        String huiFuPay = fouYouPay(payWayDO);
        if (!StringUtils.hasText(huiFuPay)) {
            channelRefundPayOrderContext.setRemark("当前支付方式不支持");
            channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(FuYouScenePayOrderExtPt.class, BizScenario.valueOf(huiFuPay), ex -> ex.queryRefundChannelPayOrder(channelRefundPayOrderContext));
    }

    @Override
    public CommonResult<PayOrderChannelResultDto> orderStatusParse(HttpServletRequest request, ProviderChannelConfigDO providerChannelConfigDO) {
        String params = request.getParameter("req");
        if (!StringUtils.hasText(params)) {
            log.info("富友从servletRequest 获取参数失败,转发到body中获取");
            return orderStatusParse(request);
        }
        Map<String, Object> stringObjectMap;
        String channelConfig = providerChannelConfigDO.getParamsConfig();
        // 配置转换
        FuYouConfig fuYouConfig = JsonUtils.parseObject(channelConfig, FuYouConfig.class);
        stringObjectMap = SignUtils.callBackXmlToObject(params);
        Object sign = stringObjectMap.get("sign");
        FuYouPayOrderResultDto fuYouPayOrderResultDto = BeanUtil.mapToBean(stringObjectMap, FuYouPayOrderResultDto.class, true);
        log.info("富友回调参数:{}", JsonUtils.toJsonString(fuYouPayOrderResultDto));
        PayOrderDO payOrderDO = processCommonCall(fuYouPayOrderResultDto);
        PayOrderChannelResultDto payOrderChannelResultDto=new PayOrderChannelResultDto();
        payOrderChannelResultDto.setUpdatePayOrderDO(payOrderDO);
        payOrderChannelResultDto.setCallBackBody(params);
        CommonResult<PayOrderChannelResultDto> payOrderDOCommonResult = CommonResult.success(payOrderChannelResultDto);
        if (sign != null) {
            try {
                boolean verifySign = SignUtils.verifySign(stringObjectMap, sign.toString(), fuYouConfig);
                if (!verifySign && checkSign) {
                    payOrderDOCommonResult.setCode(MerchantResultCode.MERCHANT_CHANNEL_CALLBACK_SIGN_ERROR.getCode());
                    return payOrderDOCommonResult;
                }
                return payOrderDOCommonResult;
            }
            catch (Exception e) {
                log.error("富友签名错误:{}", e.getMessage());
                payOrderDOCommonResult.setCode(MerchantResultCode.MERCHANT_CHANNEL_CALLBACK_SIGN_ERROR.getCode());
                return payOrderDOCommonResult;
            }
        }
        else {
            return CommonResult.error(MerchantResultCode.MERCHANT_CHANNEL_CALLBACK_SIGN_ERROR.getCode(), "sign error");
        }
    }

    @Override
    public CommonResult<PayOrderChannelResultDto> orderStatusParse(HttpServletRequest request) {
        String params = null;
        log.warn("从当前线程中获取参数失败 从流中重新获取");
        try {
            // 读取请求体中的内容
            StringBuilder stringBuilder = new StringBuilder();
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    stringBuilder.append(line);
                }
            }
            // 获取完整的 JSON 字符串
            params = stringBuilder.toString();
        }
        catch (Exception e) {
            log.error("从当前线程中获取参数失败 从流中重新获取失败:{}", e.getMessage());
            return CommonResult.error(CommonResultCode.PARAMETER_MISSING);
        }
        log.warn("从当前线程中获取参数失败 从流中重新获取:{}", params);
        Map<String, Object> paramsMap = JsonUtils.parseObject(params, Map.class);
        String req = paramsMap.get("req").toString();
        FuYouPayOrderResultDto fuYouPayOrderResultDto = JsonUtils.parseObject(req, FuYouPayOrderResultDto.class);
        PayOrderChannelResultDto payOrderChannelResultDto = processH5Call(fuYouPayOrderResultDto);
        payOrderChannelResultDto.setCallBackBody(params);
        return CommonResult.success(payOrderChannelResultDto);
    }

    @Override
    public CommonResult<PayOrderChannelResultDto> merAuthParse(HttpServletRequest request) {
        return null;
    }

    @Override
    public PayOrderDO closeOrderStatusParse(HttpServletRequest request, ProviderChannelConfigDO providerChannelConfigDO) {
        return null;
    }

    @Override
    public CommonResult<CsOrderNotifyDto> csOrderStatusParse(HttpServletRequest request, ProviderChannelConfigDO providerChannelConfigDO) {
        return null;
    }

    @Override
    public CommonResult<RefundOrderNotifyDto> refundStatusParse(HttpServletRequest request, ProviderChannelConfigDO providerChannelConfigDO) {
        return null;
    }


    @Override
    public String channelPayCode(PayWayDO payWayDO) {
        return fouYouPay(payWayDO);
    }

    @Override
    public Long queryChannelPayAppid(ChannelPayOrderContext channelPayOrderContext) {
        return null;
    }

    @Override
    public String callBackResponse(CommonResult commonResult) {
        if (commonResult.isSuccess()) {
            return "1";
        }
        else {
            return "0";
        }
    }
    @Override
    public boolean wechatLink(ChannelPayOrderContext channelPayOrderContext) {
        return false;
    }

    /**
     * 汇付支付方式路由
     *
     * @param payWayDO
     * @return
     */
    private String fouYouPay(PayWayDO payWayDO) {
        log.info("富友实际支付type:{}", payWayDO.getSubType());
        PayEnums.SCENE_PAY_TYPE scenePayType = PayEnums.SCENE_PAY_TYPE.fromByType(payWayDO.getSubType());
        if (scenePayType == null) {
            return null;
        }
        switch (scenePayType) {
            case H5:
            case APP:
                if (payWayDO.getType() == 2) {
                    //支付宝app，h5 走扫码
                    return FuYouExtConstant.NATIVE_PAY;
                }
                return FuYouExtConstant.H5_APP;
            case JSAPI:
            case MINI_APP:
                return FuYouExtConstant.MINI_PAY;
            case SCAN:
                return FuYouExtConstant.SCAN_PAY;
            // case QR:
            // return HuiFuExtConstant.SCAN_PAY;
            default:
                return FuYouExtConstant.NATIVE_PAY;
        }
    }

    /**
     * 通用回调参数解析
     * 
     * @param fuYouPayOrderResultDto
     * @return
     */
    PayOrderDO processCommonCall(FuYouPayOrderResultDto fuYouPayOrderResultDto) {
        if ("000000".equals(fuYouPayOrderResultDto.getResult_code())) {
            PayOrderDO updartePayOrderDO = new PayOrderDO();
            updartePayOrderDO.setPayOrderNo(fuYouPayOrderResultDto.getMchnt_order_no());
            switch (fuYouPayOrderResultDto.getResult_msg()) {
                case "SUCCESS":
                case "REFUND":
                    updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.SUCCESS.getCode());
                    updartePayOrderDO.setBankMsg("");
                    break;
                case "CLOSED":
                    updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.CLOSE.getCode());
                    break;
                case "USERPAYING":
                    updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.PAYING.getCode());
                    break;
                case "NOTPAY":
                    updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.PAYING.getCode());
                    break;
                case "REVOKED":
                    updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.CANCEL.getCode());
                    break;
            }
            if (StringUtils.hasText(fuYouPayOrderResultDto.getTxn_fin_ts())) {
                try {
                    updartePayOrderDO.setSuccessTime(DateUtils.shanghaiDate(fuYouPayOrderResultDto.getTxn_fin_ts()));
                }
                catch (Exception e) {
                    log.error("富友订单查询返回 时间格式化错误 dateTime:{},message:{}", fuYouPayOrderResultDto.getTxn_fin_ts(), e.getMessage());
                }
            }
            if (fuYouPayOrderResultDto.getReserved_settlement_amt() != null) {
                updartePayOrderDO.setPayAmount(MerchantUtils.fenToYuan(fuYouPayOrderResultDto.getReserved_settlement_amt()));
            }
            updartePayOrderDO.setInsOrderNo(fuYouPayOrderResultDto.getReserved_fy_trace_no());
            updartePayOrderDO.setChannelOrderNo(fuYouPayOrderResultDto.getTransaction_id());
            return updartePayOrderDO;
        }
        else {
            return null;
        }
    }

    /**
     * H5回调参数解析
     * 
     * @param fuYouPayOrderResultDto
     * @return
     */
    PayOrderChannelResultDto processH5Call(FuYouPayOrderResultDto fuYouPayOrderResultDto) {
        PayOrderChannelResultDto payOrderChannelResultDto=new PayOrderChannelResultDto();
        PayOrderDO updartePayOrderDO = new PayOrderDO();
        payOrderChannelResultDto.setUpdatePayOrderDO(updartePayOrderDO);
        updartePayOrderDO.setPayOrderNo(fuYouPayOrderResultDto.getOut_trade_no());
        // 1.支付成功，2退款成功，3撤销成功，4冲正成功
        switch (fuYouPayOrderResultDto.getPay_status()) {
            case 1:
            case 2:
            case 4:
                updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.SUCCESS.getCode());
                updartePayOrderDO.setBankMsg("");
                break;
            case 3:
                updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.CANCEL.getCode());
                break;
        }
        if (StringUtils.hasText(fuYouPayOrderResultDto.getReserved_txn_fin_ts())) {
            try {
                updartePayOrderDO.setSuccessTime(DateUtils.shanghaiDate(fuYouPayOrderResultDto.getReserved_txn_fin_ts()));
            }
            catch (Exception e) {
                log.error("富友订单查询返回 时间格式化错误 dateTime:{},message:{}", fuYouPayOrderResultDto.getReserved_txn_fin_ts(), e.getMessage());
            }
        }
        if (fuYouPayOrderResultDto.getTotal_fee() != null) {
            updartePayOrderDO.setPayAmount(MerchantUtils.fenToYuan(fuYouPayOrderResultDto.getTotal_fee()));
        }
        updartePayOrderDO.setInsOrderNo(fuYouPayOrderResultDto.getTransaction_id());
        updartePayOrderDO.setChannelOrderId(fuYouPayOrderResultDto.getReserved_channel_order_id());
        return payOrderChannelResultDto;
    }
}
